"""
Merge two documents that might be potentially duplicated.

If your Papis picker does not support selecting two items, then
pass the ``--pick`` flag to pick twice from the documents.

Examples
^^^^^^^^

-   Pick the duplicate documents:

    .. code:: sh

        papis merge "prediction and subs"

        /papers/de8d6373a8cef74b5245124ddb288d45-dejong-gerald
        /papers/0e378c330dba4a4483660388183aa648-dejong-gerald

    With the default picker and config, you can pick the two offending documents
    with ``CTRL+T`` The selected entries will have a ``#`` in the beginning of
    the document title.

    Papis will show the difference between the files and let you select which
    ones you want to keep:

    .. code:: diff

        - collections: ['t3']
        + collections: ['t3', 't3a2']

    The lines are prefixed by an indication of what will happen on accepting the
    change. Papis will remove lines prefixed with a minus sign and add those
    with a plus sign.

-   Merge the documents that match the query "prediction and subs" and commit
    the changes to git:

    .. code:: sh

        papis merge --git "prediction and subs"

-   Choose which documents to keep:

    .. code:: sh

        papis merge --keep "belief revision"

    After deciding which changes to apply, Papis will prompt for files that
    should be moved to the resulting location. The files can be chosen by
    entering the numbers that precede the documents.


Command-line interface
^^^^^^^^^^^^^^^^^^^^^^

.. click:: papis.commands.merge:cli
    :prog: papis merge
"""
from __future__ import annotations

import os
from typing import TYPE_CHECKING, Any

import click

import papis.cli
import papis.logging

if TYPE_CHECKING:
    from papis.document import Document

logger = papis.logging.get_logger(__name__)


def run(keep: Document,
        erase: Document,
        data: dict[str, Any],
        files: list[str],
        keep_both: bool,
        git: bool = False) -> None:

    files_to_move = set(files) - set(keep.get_files())
    for f in files_to_move:
        to_folder = keep.get_main_folder()
        if to_folder:
            import shutil
            logger.info("Moving '%s' to '%s'.", f, to_folder)
            shutil.copy(f, to_folder)
            keep["files"] += [os.path.basename(f)]

    from papis.commands.update import run as update
    update(keep, data, git=git)

    if not keep_both:
        from papis.commands.rm import run as rm
        from papis.document import describe

        logger.info("Removing '%s'.", describe(erase))
        rm(erase, git=git)
    else:
        logger.info("Keeping both documents.")


@click.command("merge")
@click.help_option("-h", "--help")
@papis.cli.query_argument()
@papis.cli.sort_option()
@papis.cli.bool_flag(
    "-s", "--second",
    help="Keep the second document after merge and erase the first "
         "(the default is to keep the first).")
@papis.cli.bool_flag(
    "-p", "--pick",
    help="If your picker does not support picking two documents"
         " at once, call the picker twice to get two documents.")
@papis.cli.bool_flag(
    "-k", "--keep", "keep_both",
    help="Keep both documents.")
@click.option("-o",
              "--out",
              help="Create the resulting document in this path.",
              default=None)
@papis.cli.git_option(help="Merge in git.")
def cli(query: str,
        sort_field: str | None,
        out: str | None,
        second: bool,
        git: bool,
        keep_both: bool,
        sort_reverse: bool,
        pick: bool) -> None:
    """Merge two documents from a given library."""
    from papis.database import get_database

    db = get_database()
    documents = db.query(query)

    from papis.document import describe, from_folder, sort, to_dict

    if sort_field:
        documents = sort(documents, sort_field, sort_reverse)

    if not documents:
        from papis.strings import no_documents_retrieved_message
        logger.warning(no_documents_retrieved_message)
        return

    from papis.pick import pick_doc
    documents = pick_doc(documents)

    if pick:
        other_documents = pick_doc(documents)
        documents += other_documents

    if len(documents) != 2:
        logger.error(
            "You have to pick exactly two documents (picked %d)!",
            len(documents))
        return

    a = documents[0]
    data_a = to_dict(a)
    b = documents[1]
    data_b = to_dict(b)

    to_pop = ["files"]
    for d in (data_a, data_b):
        for key in to_pop:
            if key in d:
                d.pop(key)

    from papis.utils import update_doc_from_data_interactively
    update_doc_from_data_interactively(data_a, data_b, describe(b))

    from papis.tui.utils import confirm, select_range
    files: list[str] = []
    for doc in documents:
        indices = select_range(
            doc.get_files(),
            "Documents from A to keep",
            accept_none=True,
            bottom_toolbar=describe(a))
        files += [doc.get_files()[i] for i in indices]

    if not confirm("Are you sure you want to merge?"):
        return

    keep = b if second else a
    erase = a if second else b

    if out is not None:
        import shutil

        os.makedirs(out, exist_ok=True)
        keep = from_folder(out)
        keep["files"] = []
        for f in files:
            shutil.copy(f, out)
            keep["files"] += [os.path.basename(f)]
        keep.update(data_a)
        keep.save()
        logger.info("Saving the new document in '%s'.", out)
        return

    run(keep, erase, data_a, files, keep_both, git)
